Hom関手と関数型(->)
Hom関手の良い例として関数型(->)が考えられる
これは型引数を2つ取る型コンストラクタであり、
2つのうちのいずれかを固定することで関手になることができる
Reader e aで考えても同じだが、関数型で考えたほうが直感的でわかりやすいと思うmrsekut.icon
まずはドメイン側を固定したものを見る
https://gyazo.com/9311b4c7622213a677d2518bf7e58e58
対象としての型がいくつか考えられるが、今回はIntをドメインとしての対象として固定する
すると関手(->) Intを考えられる
Intとは別の型Xを決めるごとに、新たな型(->) Int Xが得られる
(->) Int XはHaskellにおける型であるので、Hask圏の対象の一つである
(->) Int XはInt -> Xと表記することもでき、全く同じ意味であり、今回は前者を用いる
と、言いながら↓の図では後者で描いてるなmrsekut.icon
https://gyazo.com/d2b63f7f7b0b73aa64ce6da59c7baa0f
ここで例としてBoolとCharを置いてみた
(->) Intを関手と見ることで、(->) Int Boolと(->) Int Charという対象への対応が取れる
(->) Int関手はドメインもコドメインも同じHask圏であるので、これは自己関手の一種となる
対象についての対応はわかったので、射の対応についてみてみよう
射の対応は、ここでは
Int -> Boolという型の関数を一つ選択して、
f :: Bool -> Charと合成すると、
Int -> Charという型の関数となり
その対応が(->) f、つまりfmap fになるのだった
どういうことか?具体的に見ていこう
https://gyazo.com/5f7a28a3de9fcaf7c0a177850d5191cd
Int -> Boolという型の関数もいくつも考えられる
odd
even
isNegativeZero
etc.
同様にInt -> Charという型の関数もいくつも考えられる
intToDigit
chr
etc.
ここで、fを以下のように定義する
code:hs
f :: Bool -> Char
f True = 't'
f False = 'f'
例えば、Int -> Boolという型の関数から、evenを選び、fと合成する
このf . evenはInt -> Charという型の関数群の一つである
f . evenと全く同じ挙動をする関数は関数合成を用いなくとも定義できる
code:hs
fev :: Int -> Char
fev n = if odd n then 'f' else 't'
これが(->) fの対応である
つまり、Haskの写し先では
(.) fという関数を、evenに適用すると、fevが出てくる
イメージ的には((.) f) even == fev
関数型(->) rのFunctorの定義は以下のようなものだった
code:hs
instance Functor ((->) r) where
fmap = (.)
定義にもあるようにfmapは、関数合成.で定義されている
写し先のHask圏で成り立っているかを確認しよう
省略するが、残りの関手の定義である合成の保存と恒等射の保存も成り立つ
以上により、(->) Intは、Hask圏におけるHom関手であることがわかった
以下ではコドメイン側を固定した関手(->) _ Intを考える
(->)や(,)は1つ目、2つ目のどちらを固定するかで共変か反変かが変わる
「2つの型引数を取る型コンストラクタ」ではなく、↑これら2つに関しての話か。mrsekut.icon
1つ目を固定した場合
例えば(->) Intという1つの型引数を取る関手を考える
これは、共変関手となる
これは上の普通の関手の議論と同じなので省略
2つ目を固定した場合
例えば(->) _ Stringという1つの型引数を取る関手を考える
これは、反変関手になる
共変関手だと考えるとどういった不都合が生じるの?